@akalsey/openclaw-sapience 0.1.0 → 0.1.2

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.md CHANGED
@@ -6,14 +6,14 @@ The suite has four plugins that each work independently and compose into a whole
6
6
 
7
7
  | Plugin | Does |
8
8
  |--------|------|
9
- | `openclaw-proactive-thinking` | Periodic thinking passes; generates observations and proposals |
9
+ | `openclaw-thinking` | Periodic thinking passes; generates observations and proposals |
10
10
  | `openclaw-sapience` *(this plugin)* | Routes proposals through autonomy tiers; calibrates to your preferences; delivers weekly digest |
11
11
  | `openclaw-feedback` | Captures corrections and confirmations from chat; recalibrates autonomy profile |
12
12
  | `openclaw-goals` | Accepts fuzzy long-running goals; decomposes them; tracks progress; weekly status |
13
13
 
14
14
  ## How it works
15
15
 
16
- `openclaw-proactive-thinking` runs a thinking pass every 15 minutes and writes proposals to `proposals.jsonl`. `openclaw-sapience` reads that sidecar, routes each proposal through an autonomy decision function, and delivers it to your main session at the right level:
16
+ `openclaw-thinking` runs a thinking pass every 15 minutes and writes proposals to `proposals.jsonl`. `openclaw-sapience` reads that sidecar, routes each proposal through an autonomy decision function, and delivers it to your main session at the right level:
17
17
 
18
18
  - **Act** — high-confidence, reversible, low-blast-radius → done immediately, brief notification
19
19
  - **Propose** — worth doing, needs your approval → surfaces it for a yes/no
@@ -27,15 +27,15 @@ The routing decision uses a calibration profile: per-domain, per-action-class en
27
27
 
28
28
  ### Prerequisites
29
29
 
30
- Install `openclaw-proactive-thinking` first. Sapience reads its output.
30
+ Install `openclaw-thinking` first. Sapience reads its output.
31
31
 
32
32
  ### Install order
33
33
 
34
34
  ```bash
35
- openclaw plugins install local:/path/to/openclaw-proactive-thinking
36
- openclaw plugins install local:/path/to/openclaw-sapience
37
- openclaw plugins install local:/path/to/openclaw-feedback # optional
38
- openclaw plugins install local:/path/to/openclaw-goals # optional
35
+ openclaw plugins install npm:@akalsey/openclaw-thinking
36
+ openclaw plugins install npm:@akalsey/openclaw-sapience
37
+ openclaw plugins install npm:@akalsey/openclaw-feedback # optional
38
+ openclaw plugins install npm:@akalsey/openclaw-goals # optional
39
39
  ```
40
40
 
41
41
  ### Configuration (sapience)
@@ -11,6 +11,22 @@ export async function loadProcessedPasses(path) {
11
11
  return new Set();
12
12
  }
13
13
  }
14
+ export async function bootstrapProcessedPasses(proposalsPath, processedPath) {
15
+ try {
16
+ const content = await readFile(resolvePath(proposalsPath), "utf-8");
17
+ const ids = new Set(content.trim().split("\n").filter(Boolean)
18
+ .map(l => JSON.parse(l).pass_id));
19
+ if (ids.size === 0)
20
+ return ids;
21
+ const resolved = resolvePath(processedPath);
22
+ await mkdir(dirname(resolved), { recursive: true });
23
+ await writeFile(resolved, JSON.stringify({ pass_ids: [...ids] }, null, 2), "utf-8");
24
+ return ids;
25
+ }
26
+ catch {
27
+ return new Set();
28
+ }
29
+ }
14
30
  export async function markPassProcessed(passId, path, processed) {
15
31
  const updated = new Set([...processed, passId]);
16
32
  const resolved = resolvePath(path);
@@ -1,11 +1,13 @@
1
1
  // src/service.ts
2
+ import { writeFile, mkdir } from "fs/promises";
3
+ import { join } from "path";
2
4
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
5
  import { DEFAULT_CONFIG } from "./types.js";
4
6
  import { resolveDataPath, isWithinActiveHours } from "./utils.js";
5
7
  import { loadProfile, saveProfile, upsertEntry } from "./calibration.js";
6
8
  import { routeItem } from "./autonomy.js";
7
9
  import { readUnprocessedPasses, proposalSetToItems } from "./proposal-adapter.js";
8
- import { loadProcessedPasses, markPassProcessed } from "./processed-passes.js";
10
+ import { loadProcessedPasses, markPassProcessed, bootstrapProcessedPasses } from "./processed-passes.js";
9
11
  import { deliverItems } from "./delivery.js";
10
12
  import { isDigestDay, buildDigestPrompt } from "./weekly-digest.js";
11
13
  function mergeConfig(raw, workspaceDir) {
@@ -33,22 +35,27 @@ function mergeConfig(raw, workspaceDir) {
33
35
  export default definePluginEntry({
34
36
  id: "sapience",
35
37
  name: "Sapience",
36
- description: "Autonomy layer: routes proactive-thinking proposals through tier function, calibrates to human preferences, delivers weekly digest",
38
+ description: "Autonomy layer: routes sapience-thinking proposals through tier function, calibrates to human preferences, delivers weekly digest",
37
39
  register(api) {
38
40
  const workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(api.pluginConfig);
39
41
  const config = mergeConfig(api.pluginConfig, workspaceDir);
42
+ // Write presence marker so sapience-thinking knows to defer direct delivery
43
+ const markerDir = join(workspaceDir, "sapience");
44
+ void mkdir(markerDir, { recursive: true }).then(() => writeFile(join(markerDir, ".present"), "", "utf-8"));
40
45
  api.registerTool({
41
46
  name: "process_proposals",
42
- description: "Process new proposals from the proactive-thinking log and route them through the autonomy tier function. Called by the sapience cron.",
47
+ description: "Process new proposals from the sapience-thinking log and route them through the autonomy tier function. Called by the sapience cron.",
43
48
  parameters: {},
44
49
  async execute(_id, _params) {
45
50
  if (!isWithinActiveHours(config)) {
46
51
  return { content: [{ type: "text", text: "SILENT_REPLY_TOKEN" }] };
47
52
  }
48
- const [processed, profile] = await Promise.all([
49
- loadProcessedPasses(config.output.processedPassesPath),
50
- loadProfile(config.output.calibrationPath),
51
- ]);
53
+ let processed = await loadProcessedPasses(config.output.processedPassesPath);
54
+ const profile = await loadProfile(config.output.calibrationPath);
55
+ // On first run, mark all existing passes as processed to avoid re-delivering stale proposals
56
+ if (processed.size === 0) {
57
+ processed = await bootstrapProcessedPasses(config.proactiveThinking.proposalsPath, config.output.processedPassesPath);
58
+ }
52
59
  const newPasses = await readUnprocessedPasses(config.proactiveThinking.proposalsPath, processed);
53
60
  let updatedProcessed = processed;
54
61
  let updatedProfile = profile;
@@ -2,7 +2,7 @@
2
2
  "id": "sapience",
3
3
  "name": "Sapience",
4
4
  "version": "0.1.0",
5
- "description": "Autonomy layer: routes proactive-thinking proposals through tier function, calibrates to human preferences, delivers weekly digest",
5
+ "description": "Autonomy layer: routes sapience-thinking proposals through tier function, calibrates to human preferences, delivers weekly digest",
6
6
  "contracts": {
7
7
  "tools": ["process_proposals"]
8
8
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akalsey/openclaw-sapience",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {