helpers

package
v1.0.44 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 49 Imported by: 0

Documentation

Overview

Package helpers — readable-output enrichment for `report inbox list` and `report outbox list`.

What this file adds:

  • EnrichReportListContent: pure function that walks an MCP `content` response, finds the report items list, and overlays five extra fields the agent layer relies on: success count agentDisplayContentIncluded (bool — inbox false, outbox true) agentDisplayColumns ([]string — wukong-aligned column set) agentDisplayMarkdown (string — markdown table) The function never mutates the input map in-place; it returns a fresh map so callers (including tests) can compare before/after safely.

  • AttachReportListReadableEnrichment: post-merge hook that finds the dynamic-built `report inbox list` / `report outbox list` leaves in the merged command tree and wraps their RunE to apply EnrichReportListContent to the JSON payload before it is written to stdout. Behaviour for other formats (--format raw / table / csv etc.) is preserved verbatim — enrichment only fires when the output is JSON.

Why a post-merge hook (mirroring AttachReportLegacyInboxAlias):

the envelope already publishes `report inbox` and `report outbox` as
groups with a `list` leaf each; the open-source CLI cannot add a same-
named helper leaf (MergeHardcodedLeaves would reject it as a shape
mismatch with the envelope). Wrapping the existing leaf's RunE keeps the
envelope as the single source of truth for flags/schema while letting us
layer wukong-equivalent enrichment on top.

Index

Constants

View Source
const RoleConfigExample = `` /* 858-byte string literal not displayed */

RoleConfigExample is a complete, copy-pasteable role definition users can follow when authoring their own. It is also parsed in tests to keep the example honest against the schema.

Variables

This section is empty.

Functions

func AtomicWrite

func AtomicWrite(path string, data []byte, perm os.FileMode) error

AtomicWrite writes data to path atomically by creating a temp file in the same directory, writing and fsyncing the data, then renaming over the target. It replaces os.WriteFile for all config and download file writes.

os.WriteFile truncates the target before writing, so a process kill (CI timeout, OOM, Ctrl+C) between truncate and completion leaves the file empty or partial. AtomicWrite avoids this: on any failure the temp file is cleaned up and the original file remains untouched.

func AtomicWriteFromReader

func AtomicWriteFromReader(path string, reader io.Reader, perm os.FileMode) (int64, error)

AtomicWriteFromReader atomically copies reader contents into path.

func AtomicWriteJSON

func AtomicWriteJSON(path string, data []byte) error

AtomicWriteJSON is a convenience wrapper for writing JSON data atomically. It uses 0600 permissions by default for sensitive data.

func AttachReportLegacyInboxAlias added in v1.0.33

func AttachReportLegacyInboxAlias(commands []*cobra.Command, runner executor.Runner)

AttachReportLegacyInboxAlias finds the dynamic-built `report inbox` group in the merged command tree and turns it into a dual-role command: when invoked with flags (and no subcommand), it executes the canonical inbox list handler with a deprecation notice on stderr. Sub-command invocation (e.g. `report inbox list ...`) keeps working unchanged.

Why a post-merge hook: the envelope publishes `inbox` as a group whose only child is `list`. Hardcoded helpers cannot graft a same-named leaf in (MergeHardcodedLeaves rejects helper-leaf vs envelope-group as a shape mismatch), so we instead enrich the existing group's flags and RunE in place. The `runner` is the same executor that the helper leaves use, keeping behaviour identical to `report list` / `report inbox list`.

func AttachReportListReadableEnrichment added in v1.0.33

func AttachReportListReadableEnrichment(commands []*cobra.Command, runner executor.Runner)

AttachReportListReadableEnrichment wires EnrichReportListContent into the envelope-built `report inbox list` and `report outbox list` leaves.

Mechanism (decorator over the leaf's existing RunE):

  1. Replace leaf.RunE with a closure that delegates to the original RunE.
  2. Before delegating, redirect cmd.SetOut() to an in-memory buffer when the resolved output format is JSON (the only format the agent display schema cares about).
  3. After the original RunE returns, unmarshal the captured bytes, locate the MCP `content` payload, call EnrichReportListContent, and write the enriched JSON to the leaf's original stdout.
  4. For non-JSON formats (raw / table / csv / ...) we never replace stdout, so behaviour stays byte-for-byte identical to the unwrapped envelope.

`runner` is accepted for API symmetry with AttachReportLegacyInboxAlias — the wrapper itself does not invoke runner; the wrapped RunE already does. Keeping the parameter avoids a churn on app/legacy.go if we ever need to dispatch a sibling tool from inside the wrapper.

func EnrichReportListContent added in v1.0.33

func EnrichReportListContent(content map[string]any, includeContent bool) map[string]any

EnrichReportListContent overlays wukong-style agent-display fields on the MCP `content` map of a report list response. It is a pure function:

  • input map is never mutated;
  • on any structural mismatch (nil, no list, etc.) it returns the input untouched so the caller's behaviour is byte-stable.

includeContent controls whether the "日志内容" column appears in agentDisplayColumns / agentDisplayMarkdown and whether each result row retains the 日志内容 key. Pass true for outbox (sender == self, content safe to echo), false for inbox.

func LoadRoleConfigs added in v1.0.42

func LoadRoleConfigs(dir string) (map[string]*RoleConfig, error)

LoadRoleConfigs loads every *.yaml / *.yml file directly under dir and indexes the roles by ClientID — the lookup a multi-bot, one-role-per-bot deployment needs. A duplicate ClientID is an error: two roles must not share one bot.

func NewPublicCommands

func NewPublicCommands(runner executor.Runner) []*cobra.Command

func NormalizeSkillName

func NormalizeSkillName(input string) string

NormalizeSkillName converts free-form skill names to a stable dash-case key.

func RegisterPublic

func RegisterPublic(factory Factory)

func SetCmdClassOverride added in v1.0.42

func SetCmdClassOverride(key string, class CmdClass)

SetCmdClassOverride registers a process-wide classification override for the given key. The key may be a single verb or a space-joined command path; it is normalised to lowercase. Passing CmdClassUnknown removes the override.

func ValidateNaming

func ValidateNaming(vendor, name string) error

Types

type ApprovalRequest added in v1.0.42

type ApprovalRequest struct {
	ID        string        `json:"id"`
	Requester string        `json:"requester"`      // staffId of who asked
	ConvID    string        `json:"conv_id"`        // conversation to reply into
	Summary   string        `json:"summary"`        // human-readable "what will happen"
	Verb      string        `json:"verb,omitempty"` // action verb, e.g. "todo.create" (remember key)
	Action    plannedAction `json:"action"`         // structured command to run on approve
	State     approvalState `json:"state"`
	// OutTrackID is the delivered card's instance id, recorded so a button
	// callback that only carries the card id (not the approval id in its action
	// params) can still be mapped back to this request.
	OutTrackID string `json:"out_track_id,omitempty"`
	DecidedBy  string `json:"decided_by,omitempty"` // staffId who approved/rejected
	ExecErr    string `json:"exec_err,omitempty"`   // failure detail when State=failed
	// AutoApproved marks a request the owner made of THEMSELVES: no second
	// confirmation is asked (the owner asking IS the authorization), but the
	// full record is still persisted for audit, with DecidedBy=owner. Lets the
	// audit trail distinguish an auto-run from an explicitly-confirmed one.
	AutoApproved bool      `json:"auto_approved,omitempty"`
	CreatedAt    time.Time `json:"created_at"`
	DecidedAt    time.Time `json:"decided_at,omitempty"`
}

ApprovalRequest is one pending/decided confirmation. It is the on-disk record too (marshalled as-is), so every field is JSON-tagged and self-describing.

type CmdClass added in v1.0.42

type CmdClass int

CmdClass describes whether a dws command is read-only or mutating. It is the signal a connector confirmation gate consumes to decide whether a robot may run a command directly (read-only) or must first ask the principal to approve it (write / mutating).

SAFETY CONTRACT: callers MUST treat CmdClassUnknown conservatively, i.e. as if it were CmdClassWrite (require confirmation). The classifier deliberately returns Unknown rather than silently coercing it to Write so that the confirmation gate keeps full information and can, for example, log/telemeter "unclassified" commands separately. Never auto-allow an Unknown command.

const (
	// CmdClassUnknown means the leaf verb was not recognised by the
	// heuristics or any override. Callers must default to requiring
	// confirmation (treat as write) for safety.
	CmdClassUnknown CmdClass = iota
	// CmdClassRead is a read-only / non-mutating command that may be run
	// without principal confirmation.
	CmdClassRead
	// CmdClassWrite is a mutating / state-changing command that must be
	// confirmed by the principal before it runs.
	CmdClassWrite
)

func ClassifyDwsCommand added in v1.0.42

func ClassifyDwsCommand(parts ...string) CmdClass

ClassifyDwsCommand classifies a dws command given its path segments (e.g. "todo", "task", "create"). It consults the package-level override table and then the read/write verb heuristics, scanning segments right-to-left so the leaf action verb dominates a container/noun segment.

Remember the SAFETY CONTRACT on CmdClass: a CmdClassUnknown result MUST be treated as write (require confirmation) by the caller.

func ClassifyDwsCommandWith added in v1.0.42

func ClassifyDwsCommandWith(overrides map[string]CmdClass, parts ...string) CmdClass

ClassifyDwsCommandWith is like ClassifyDwsCommand but lets the caller supply an explicit override table (e.g. a per-tenant or per-request map) instead of the package-level one. A nil overrides map is allowed and means "no overrides". Override lookups always win over the heuristics.

func (CmdClass) String added in v1.0.42

func (c CmdClass) String() string

String renders the class as a stable lowercase token, handy for logs and telemetry.

type ConfirmPolicy added in v1.0.42

type ConfirmPolicy string

ConfirmPolicy controls how a role asks its owner before taking an action.

const (
	// ConfirmManual asks the owner to confirm every action.
	ConfirmManual ConfirmPolicy = "manual"
	// ConfirmAuto lets the bot judge for itself whether confirmation is needed.
	ConfirmAuto ConfirmPolicy = "auto"
	// ConfirmRemember reuses the owner's previous choice for the same kind of
	// operation.
	ConfirmRemember ConfirmPolicy = "remember"
)

type Factory

type Factory func() Handler

type Handler

type Handler interface {
	Name() string
	Command(runner executor.Runner) *cobra.Command
}

type Manifest

type Manifest struct {
	Vendor      string
	Name        string
	Description string
}

func (Manifest) FullName

func (m Manifest) FullName() string

type RoleConfig added in v1.0.42

type RoleConfig struct {
	// Name is the human-facing role name, e.g. "人事助理".
	Name string `yaml:"name"`
	// ClientID is the DingTalk bot clientId this role is bound to. One role owns
	// exactly one bot, so this is also the unique key across a role set.
	ClientID string `yaml:"client_id"`
	// Persona is a professional prompt fragment merged into the agent's system
	// prompt to shape tone and expertise.
	Persona string `yaml:"persona"`
	// KnowledgeSources lists knowledge sources using the existing
	// --knowledge-source syntax: a bare path is a local directory, "wiki:<spaceId>"
	// a whole knowledge space, "doc:<docId>" a single document.
	KnowledgeSources []string `yaml:"knowledge_sources"`
	// AllowedScopes names the dws capabilities/products this role may use, e.g.
	// ["todo", "approval", "attendance"]. Consumed by later permission checks.
	AllowedScopes []string `yaml:"allowed_scopes"`
	// OwnerUserID is the userId of the role's owner; confirmation requests go here.
	OwnerUserID string `yaml:"owner_user_id"`
	// ConfirmPolicy selects the confirmation strategy; empty defaults to manual.
	ConfirmPolicy ConfirmPolicy `yaml:"confirm_policy"`
	// Extra is an open-ended bag for forward-compatible keys, so the schema can
	// grow without a breaking change. Intentionally minimal — not a config DSL.
	Extra map[string]string `yaml:"extra"`
}

RoleConfig is the on-disk (YAML) definition of one digital-employee role. It maps 1:1 to a single bot via ClientID.

func LoadRoleConfig added in v1.0.42

func LoadRoleConfig(path string) (*RoleConfig, error)

LoadRoleConfig reads, parses and validates a single role YAML file.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL