dsh-agentic-pet 0.4.8 → 0.4.10
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/carrier/lib/main.js +48 -2
- package/client/pet-settings.js +327 -14
- package/lib/{service-Df8olC1E.js → driver-CpRgkle-.js} +3391 -1775
- package/lib/index.js +467 -48
- package/lib/tools.js +3 -3
- package/lib/types/actions.d.ts +28 -0
- package/lib/types/binding.d.ts +99 -0
- package/lib/types/driver.d.ts +42 -2
- package/lib/types/electron-lock.d.ts +25 -0
- package/lib/types/electron-runtime-status.d.ts +20 -0
- package/lib/types/inner-voice.d.ts +91 -1
- package/lib/types/journal.d.ts +58 -0
- package/lib/types/presence.d.ts +27 -0
- package/lib/types/registry.d.ts +1 -1
- package/lib/types/rhythm.d.ts +21 -0
- package/lib/types/service.d.ts +6 -0
- package/lib/types/settings-page.d.ts +2 -1
- package/lib/types/tools.d.ts +2 -2
- package/package.json +3 -3
- package/presets/pet-inner-voice.patch.yml +3 -0
- package/presets/pet.patch.yml +1 -0
package/lib/types/actions.d.ts
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
* catalog drift between pet implementations must die.
|
|
7
7
|
* @module dsh-agentic-pet/actions
|
|
8
8
|
*/
|
|
9
|
+
/** One body-owned catalog entry. `tags` are THIS body's vocabulary — the
|
|
10
|
+
* driver never defines a cross-body enum. */
|
|
11
|
+
export interface ActionCatalogEntry {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly tags: readonly string[];
|
|
14
|
+
readonly energy?: 'low' | 'mid' | 'high';
|
|
15
|
+
readonly durationHintMs?: number;
|
|
16
|
+
readonly textInFrame?: boolean;
|
|
17
|
+
}
|
|
9
18
|
/** A pet's advertised capabilities. `contractVersion: null` = a legacy
|
|
10
19
|
* implementation that predates the version field (v1 semantics: seven
|
|
11
20
|
* endpoints, no /world). `actions: null` = the pet publishes no catalog (a
|
|
@@ -14,6 +23,10 @@
|
|
|
14
23
|
export interface PetCapabilities {
|
|
15
24
|
readonly contractVersion: number | null;
|
|
16
25
|
readonly actions: readonly string[] | null;
|
|
26
|
+
readonly actionCatalog: readonly ActionCatalogEntry[] | null;
|
|
27
|
+
/** The body's own tag vocabulary, derived from its catalog when omitted. */
|
|
28
|
+
readonly actionTagVocabulary: readonly string[];
|
|
29
|
+
readonly tagged: boolean;
|
|
17
30
|
}
|
|
18
31
|
/** Defensive extraction from a raw /capabilities payload: implementations
|
|
19
32
|
* may omit or mistype anything. */
|
|
@@ -39,3 +52,18 @@ export declare function resolveActionRequest(requested: string, catalog: readonl
|
|
|
39
52
|
/** The error-message menu: long catalogs are previewed, with a pointer to
|
|
40
53
|
* pet_state {actions:true} for the full list. */
|
|
41
54
|
export declare function formatAvailableActions(available: readonly string[], cap?: number): string;
|
|
55
|
+
export type TagResolution = {
|
|
56
|
+
kind: 'ok';
|
|
57
|
+
action: string;
|
|
58
|
+
candidates: readonly string[];
|
|
59
|
+
} | {
|
|
60
|
+
kind: 'unknown';
|
|
61
|
+
availableTags: readonly string[];
|
|
62
|
+
availableActions: readonly string[];
|
|
63
|
+
};
|
|
64
|
+
/** Resolve a tag against the CURRENT body's own catalog. Multiple matches are
|
|
65
|
+
* a random draw (celebrate is never a tag candidate). No cross-body tag
|
|
66
|
+
* vocabulary is consulted. */
|
|
67
|
+
export declare function resolveTagRequest(tag: string, capabilities: PetCapabilities | null): TagResolution;
|
|
68
|
+
/** Tag menu for errors: an empty vocabulary names the fallback explicitly. */
|
|
69
|
+
export declare function formatAvailableTags(tags: readonly string[], cap?: number): string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binding facts shared by the driver and the settings page: preset
|
|
3
|
+
* validation, attach-failure classification, path containment, and the
|
|
4
|
+
* public diagnostics shape. Pure functions only — the mutable owner map
|
|
5
|
+
* stays in the driver.
|
|
6
|
+
* @module dsh-agentic-pet/binding
|
|
7
|
+
*/
|
|
8
|
+
/** Presets that mount the pet tool row (`pet-tools`). */
|
|
9
|
+
export declare const PET_PRESET_IDS: ReadonlySet<string>;
|
|
10
|
+
export declare function isPetPreset(preset: string | undefined | null): boolean;
|
|
11
|
+
export type VoiceAttachErrorCode = 'none' | 'cwd-mismatch' | 'registry-missing' | 'session-archived' | 'session-missing' | 'preset-mismatch' | 'attach-failed';
|
|
12
|
+
export interface VoiceAttachState {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
errorCode: VoiceAttachErrorCode;
|
|
15
|
+
error: string | null;
|
|
16
|
+
expectedCwd: string;
|
|
17
|
+
actualCwd: string | null;
|
|
18
|
+
at: number;
|
|
19
|
+
}
|
|
20
|
+
export interface VoiceBindingView {
|
|
21
|
+
source: 'boundSessions' | 'innerVoiceStore' | 'none';
|
|
22
|
+
configuredSessionId: string | null;
|
|
23
|
+
expectedSessionId: string | null;
|
|
24
|
+
effectiveSessionId: string | null;
|
|
25
|
+
storeSessionId: string | null;
|
|
26
|
+
usable: boolean;
|
|
27
|
+
/** The CURRENT preset (live projection or projection cache), not the
|
|
28
|
+
* session header's creation-time value. */
|
|
29
|
+
preset: string | null;
|
|
30
|
+
presetWarning: boolean;
|
|
31
|
+
cwd: string | null;
|
|
32
|
+
lastError: string | null;
|
|
33
|
+
armed: boolean;
|
|
34
|
+
armedAt: number | null;
|
|
35
|
+
attach: VoiceAttachState | null;
|
|
36
|
+
owners: string[];
|
|
37
|
+
staleOwners: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface VoiceBindingFailure {
|
|
40
|
+
at: number;
|
|
41
|
+
petId: string;
|
|
42
|
+
sessionId: string;
|
|
43
|
+
errorCode: VoiceAttachErrorCode;
|
|
44
|
+
error: string;
|
|
45
|
+
expectedCwd: string;
|
|
46
|
+
actualCwd: string | null;
|
|
47
|
+
}
|
|
48
|
+
export interface VoiceBindingSnapshot {
|
|
49
|
+
updatedAt: number;
|
|
50
|
+
pets: Record<string, VoiceBindingView>;
|
|
51
|
+
failures: VoiceBindingFailure[];
|
|
52
|
+
}
|
|
53
|
+
export interface SessionHeaderInfo {
|
|
54
|
+
id?: string;
|
|
55
|
+
agentPreset?: string;
|
|
56
|
+
cwd?: string;
|
|
57
|
+
title?: string;
|
|
58
|
+
}
|
|
59
|
+
/** Structural face of the host's live Session store (`ctx.get('sessions')`).
|
|
60
|
+
* `get()` only sees LIVE sessions; a stored-but-not-open binding is found
|
|
61
|
+
* through {@link SessionPersistenceFace} instead. */
|
|
62
|
+
export interface SessionStoreFace {
|
|
63
|
+
get(sessionId: string): {
|
|
64
|
+
header?: SessionHeaderInfo;
|
|
65
|
+
} | undefined;
|
|
66
|
+
}
|
|
67
|
+
/** Structural face of the host's session persistence listing. */
|
|
68
|
+
export interface SessionPersistenceFace {
|
|
69
|
+
list(options?: unknown): Promise<readonly {
|
|
70
|
+
header?: SessionHeaderInfo;
|
|
71
|
+
}[]>;
|
|
72
|
+
}
|
|
73
|
+
/** Structural face of the host's preset registry. `presetForSession` reads
|
|
74
|
+
* the session's committed `agent-preset/selected` projection — the current
|
|
75
|
+
* mode, unlike the immutable creation-time header field. */
|
|
76
|
+
export interface AgentPresetFace {
|
|
77
|
+
presetForSession(session: unknown): string | null;
|
|
78
|
+
}
|
|
79
|
+
/** Structural face of the zero-I/O projection cache, used to read the
|
|
80
|
+
* current preset of a stored-but-not-open session. */
|
|
81
|
+
export interface SessionProjectionCacheFace {
|
|
82
|
+
cachedSnapshot(meta: unknown): {
|
|
83
|
+
values?: {
|
|
84
|
+
agentPreset?: unknown;
|
|
85
|
+
};
|
|
86
|
+
} | undefined;
|
|
87
|
+
}
|
|
88
|
+
/** Parse the host's attachSession rejection into a stable code. The cwd is
|
|
89
|
+
* the only field worth surfacing verbatim; everything else is a category. */
|
|
90
|
+
export declare function classifyAttachFailure(message: string): {
|
|
91
|
+
errorCode: VoiceAttachErrorCode;
|
|
92
|
+
actualCwd: string | null;
|
|
93
|
+
};
|
|
94
|
+
/** Whether this settings patch actually edits the electron binding. Unrelated
|
|
95
|
+
* saves must not be hard-rejected because an EXISTING binding is invalid. */
|
|
96
|
+
export declare function patchTouchesElectronBinding(patch: Record<string, unknown>): boolean;
|
|
97
|
+
/** The host attaches a session only when its cwd IS the workspace path
|
|
98
|
+
* (canonical equality), not a descendant. */
|
|
99
|
+
export declare function isPetWorkspace(cwd: string | undefined | null, root: string): boolean;
|
package/lib/types/driver.d.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* @module dsh-agentic-pet/driver
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
|
-
import type
|
|
10
|
+
import { type VoiceBindingSnapshot, type VoiceBindingView } from './binding.ts';
|
|
11
|
+
import { type PetService } from './service.ts';
|
|
11
12
|
declare module '@deepseek-ai/cordis' {
|
|
12
13
|
interface Events {
|
|
13
14
|
'api-session/status'(sessionId: string, running: boolean): void;
|
|
@@ -15,6 +16,25 @@ declare module '@deepseek-ai/cordis' {
|
|
|
15
16
|
'api-session/error'(sessionId: string, message: string): void;
|
|
16
17
|
'api-session/removed'(sessionId: string): void;
|
|
17
18
|
'pet-settings/changed'(): void;
|
|
19
|
+
'pet-binding/failed'(payload: {
|
|
20
|
+
petId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
errorCode: string;
|
|
23
|
+
expectedCwd: string;
|
|
24
|
+
actualCwd: string | null;
|
|
25
|
+
at: number;
|
|
26
|
+
}): void;
|
|
27
|
+
'pet-binding/armed'(payload: {
|
|
28
|
+
petId: string;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
at: number;
|
|
31
|
+
}): void;
|
|
32
|
+
'pet-binding/rebound'(payload: {
|
|
33
|
+
petId: string;
|
|
34
|
+
fromSessionId: string;
|
|
35
|
+
toSessionId: string | null;
|
|
36
|
+
at: number;
|
|
37
|
+
}): void;
|
|
18
38
|
'approval/request'(request: {
|
|
19
39
|
toolName?: string;
|
|
20
40
|
reason?: string;
|
|
@@ -23,6 +43,11 @@ declare module '@deepseek-ai/cordis' {
|
|
|
23
43
|
readonly [locale: string]: string | undefined;
|
|
24
44
|
};
|
|
25
45
|
signal?: AbortSignal;
|
|
46
|
+
/** Host-side correlation (the asking controller's agent identity).
|
|
47
|
+
* Read defensively — the ledger records null when absent. */
|
|
48
|
+
agent?: {
|
|
49
|
+
id?: unknown;
|
|
50
|
+
};
|
|
26
51
|
}, next: () => Promise<unknown>): Promise<unknown>;
|
|
27
52
|
'user-questions/request'(request: {
|
|
28
53
|
questions: Array<{
|
|
@@ -37,6 +62,10 @@ declare module '@deepseek-ai/cordis' {
|
|
|
37
62
|
multiSelect?: boolean;
|
|
38
63
|
}>;
|
|
39
64
|
signal?: AbortSignal;
|
|
65
|
+
/** Host-side correlation — see approval/request. */
|
|
66
|
+
agent?: {
|
|
67
|
+
id?: unknown;
|
|
68
|
+
};
|
|
40
69
|
}, next: () => Promise<unknown>): Promise<unknown>;
|
|
41
70
|
}
|
|
42
71
|
}
|
|
@@ -44,7 +73,18 @@ declare module '@deepseek-ai/cordis' {
|
|
|
44
73
|
* tool, so timestamps show the USER's wall clock, not UTC (toISOString's Z
|
|
45
74
|
* form read 8h off and cost a confusion round). Sortable, zero-padded. */
|
|
46
75
|
export declare const logStamp: () => string;
|
|
47
|
-
|
|
76
|
+
/** Export for the settings page: the live snapshot, no file round-trip. */
|
|
77
|
+
export declare function voiceBindingSnapshot(): VoiceBindingSnapshot;
|
|
78
|
+
export interface PetDriverControl {
|
|
79
|
+
bindingSnapshot(): VoiceBindingSnapshot;
|
|
80
|
+
testBinding(petId: string): Promise<{
|
|
81
|
+
ok: boolean;
|
|
82
|
+
petId: string;
|
|
83
|
+
sessionId: string | null;
|
|
84
|
+
binding: VoiceBindingView;
|
|
85
|
+
}>;
|
|
86
|
+
}
|
|
87
|
+
export declare function installPetDriver(ctx: Context, home: string, pet: PetService): PetDriverControl;
|
|
48
88
|
/**
|
|
49
89
|
* The model-expression layer: three thin tools over the pet's control
|
|
50
90
|
* interface (§6 — accurate state stays program-driven; these carry only the
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install-lock policy for the npm Electron bootstrap. The lock is a plain
|
|
3
|
+
* file; liveness of the recorded owner is the primary staleness signal, so a
|
|
4
|
+
* host restart during a download cannot block the next activation for a fixed
|
|
5
|
+
* grace period. Time-based expiry remains only as a backstop for PID reuse,
|
|
6
|
+
* malformed locks, and an owner whose event loop is stuck.
|
|
7
|
+
* @module dsh-agentic-pet/electron-lock
|
|
8
|
+
*/
|
|
9
|
+
export declare const INSTALL_LOCK_STALE_MS: number;
|
|
10
|
+
export declare const INSTALL_LOCK_MALFORMED_GRACE_MS = 60000;
|
|
11
|
+
export interface InstallLockRecord {
|
|
12
|
+
pid?: unknown;
|
|
13
|
+
at?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/** True when `pid` names a live process (EPERM means it exists but is not ours). */
|
|
16
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
17
|
+
/** Parse a lock file; `undefined` means empty/malformed. */
|
|
18
|
+
export declare function parseInstallLock(raw: string): InstallLockRecord | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Whether an existing lock may be taken over by a new installer.
|
|
21
|
+
* A dead owner is stale immediately; a live owner keeps its lock until the
|
|
22
|
+
* absolute cap (which also bounds PID-reuse false positives). A malformed
|
|
23
|
+
* record gets a short grace for a concurrent writer, then goes stale.
|
|
24
|
+
*/
|
|
25
|
+
export declare function installLockStale(raw: string | undefined, mtimeMs: number, now: number, alive?: (pid: number) => boolean): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted state of the Electron runtime bootstrap, shared by the installer
|
|
3
|
+
* path (writer) and the settings page (reader). The settings page is the only
|
|
4
|
+
* user-visible surface while the first install downloads ~158 MB, so this
|
|
5
|
+
* file is deliberately plain JSON in the pet home rather than process state.
|
|
6
|
+
* @module dsh-agentic-pet/electron-runtime-status
|
|
7
|
+
*/
|
|
8
|
+
export type ElectronRuntimeState = 'preparing' | 'downloading' | 'ready' | 'failed' | 'busy' | 'unavailable';
|
|
9
|
+
export interface ElectronRuntimeStatus {
|
|
10
|
+
state: ElectronRuntimeState;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
startedAt?: number;
|
|
13
|
+
finishedAt?: number;
|
|
14
|
+
attempt?: number;
|
|
15
|
+
source?: 'npmmirror' | 'github' | 'explicit';
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function electronRuntimeStatusPath(home: string): string;
|
|
19
|
+
export declare function readElectronRuntimeStatus(home: string): ElectronRuntimeStatus | undefined;
|
|
20
|
+
export declare function writeElectronRuntimeStatus(home: string, status: ElectronRuntimeStatus): void;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* SessionController calls, and the per-pet pending locks.
|
|
7
7
|
* @module dsh-agentic-pet/inner-voice
|
|
8
8
|
*/
|
|
9
|
+
import { type PresenceFacts } from './presence.ts';
|
|
9
10
|
/** Durable petId → sessionId mapping for the inner-voice sessions. */
|
|
10
11
|
export interface InnerVoiceStore {
|
|
11
12
|
readonly sessions: Record<string, {
|
|
@@ -51,6 +52,13 @@ export interface InnerVoiceConfig {
|
|
|
51
52
|
* 'fire' = 到点必醒(计划是模型自己的决定,不带礼貌门);'defer' =
|
|
52
53
|
* 忙时顺延——计划放回待执行集,下一个扫描拍重试,永不丢弃。 */
|
|
53
54
|
selfWakeBusyPolicy: SelfWakeBusyPolicy;
|
|
55
|
+
/** 宠物间消息(pet_say 的 audience 指向另一只宠)的投递方式:
|
|
56
|
+
* 'queue' = 进收件箱,目标下次唤醒时 prompt 报数、pet_inbox 取文(默认,
|
|
57
|
+
* 像活物不刷屏);'immediate' = 气泡 5s 后立即唤醒目标投递。 */
|
|
58
|
+
petToPetDelivery: 'immediate' | 'queue';
|
|
59
|
+
/** 宠物间定向消息的分档限频上限(滑动窗口,一小时内):任一档超了即拒,
|
|
60
|
+
* 四个数值用户可配。 */
|
|
61
|
+
petMessageLimits: PetMessageLimits;
|
|
54
62
|
/** Manually bound sessions (petId → sessionId): the user's own
|
|
55
63
|
* conversations the inner voice should live in — highest priority, used
|
|
56
64
|
* as-is (no creation, no rename, never auto-retired). Empty = automatic
|
|
@@ -94,6 +102,64 @@ export declare function takeNearDuePlan(list: WakePlan[], now: number, windowMs?
|
|
|
94
102
|
* the catch-up wake says 「错过了 N 个计划」 and lists them together instead
|
|
95
103
|
* of bombing one wake per missed plan. */
|
|
96
104
|
export declare function missedPlans(due: WakePlan[], bootAt: number): WakePlan[];
|
|
105
|
+
/** One queued pet-to-pet message. Bubbles are on the user's desktop — this
|
|
106
|
+
* is表演, not a private channel. */
|
|
107
|
+
export interface PetMessage {
|
|
108
|
+
from: string;
|
|
109
|
+
text: string;
|
|
110
|
+
at: number;
|
|
111
|
+
}
|
|
112
|
+
export declare const PET_MESSAGES_CAP = 20;
|
|
113
|
+
export declare function petMessagesStorePath(home: string): string;
|
|
114
|
+
/** Load every pet's unread inbox: current → legacy → {} (defensive like the
|
|
115
|
+
* other stores). */
|
|
116
|
+
export declare function loadPetMessages(home: string): Promise<Record<string, PetMessage[]>>;
|
|
117
|
+
export declare function savePetMessages(home: string, messages: Record<string, PetMessage[]>): Promise<void>;
|
|
118
|
+
/** Append one message to a pet's inbox, oldest dropped past the cap (a
|
|
119
|
+
* neglected inbox cannot grow without bound). */
|
|
120
|
+
export declare function queuePetMessage(list: PetMessage[], message: PetMessage): PetMessage[];
|
|
121
|
+
/** Consume the WHOLE inbox (pet_inbox 取文即清空; the wake prompt only
|
|
122
|
+
* announces counts). Returns the taken messages and the emptied tail. */
|
|
123
|
+
export declare function takePetMessages(list: PetMessage[]): {
|
|
124
|
+
taken: PetMessage[];
|
|
125
|
+
next: PetMessage[];
|
|
126
|
+
};
|
|
127
|
+
export interface PetMessageLimits {
|
|
128
|
+
minutes5: number;
|
|
129
|
+
minutes10: number;
|
|
130
|
+
minutes30: number;
|
|
131
|
+
minutes60: number;
|
|
132
|
+
}
|
|
133
|
+
export declare const DEFAULT_PET_MESSAGE_LIMITS: PetMessageLimits;
|
|
134
|
+
export type PetMessageRateVerdict = {
|
|
135
|
+
ok: true;
|
|
136
|
+
recent: number[];
|
|
137
|
+
} | {
|
|
138
|
+
ok: false;
|
|
139
|
+
windowMinutes: number;
|
|
140
|
+
cap: number;
|
|
141
|
+
count: number;
|
|
142
|
+
retryAfterSeconds: number;
|
|
143
|
+
};
|
|
144
|
+
/** The tiered sliding-window check for one from→to pair. `timestamps` are the
|
|
145
|
+
* pair's recent send times; the hour window prunes them (静默一小时后自然
|
|
146
|
+
* 重新开始), then each tier rejects when its window already holds the cap.
|
|
147
|
+
* The first (narrowest) violated tier is reported, with the earliest moment
|
|
148
|
+
* a slot frees up. `recent` on success = the pruned list — the caller appends
|
|
149
|
+
* its own timestamp after the send succeeds. */
|
|
150
|
+
export declare function checkPetMessageRate(timestamps: number[], now: number, limits: PetMessageLimits): PetMessageRateVerdict;
|
|
151
|
+
/** The immediate-delivery prompt (petToPetDelivery='immediate'): the target
|
|
152
|
+
* is woken right away with the sender's name and id — the id because the
|
|
153
|
+
* reply path is pet_say audience="<fromId>". The target's current affection
|
|
154
|
+
* toward the sender rides along so the reply carries history, not a blank
|
|
155
|
+
* slate. Responding or ignoring is the target's own call, exactly like any
|
|
156
|
+
* other wake. */
|
|
157
|
+
export declare function buildCrossPetPrompt(facts: {
|
|
158
|
+
fromName: string;
|
|
159
|
+
fromId: string;
|
|
160
|
+
text: string;
|
|
161
|
+
affection: number;
|
|
162
|
+
}): string;
|
|
97
163
|
export declare function loadInnerVoiceStore(home: string): Promise<InnerVoiceStore>;
|
|
98
164
|
export declare function saveInnerVoiceStore(home: string, store: InnerVoiceStore): Promise<void>;
|
|
99
165
|
export declare function voiceSessionIdFor(petId: string): string;
|
|
@@ -120,6 +186,10 @@ export interface GreetPromptFacts {
|
|
|
120
186
|
bondValue: number;
|
|
121
187
|
bondDays: number;
|
|
122
188
|
bondTier: string;
|
|
189
|
+
/** 用户起的名字(未起则内置默认):自我介绍的依据,改名后第一天尤其重要。 */
|
|
190
|
+
name: string;
|
|
191
|
+
/** 用户刚从一段离开中回来:最多一行的期间摘要。 */
|
|
192
|
+
presence?: PresenceFacts;
|
|
123
193
|
}
|
|
124
194
|
/** The boot-greeting prompt: fired once per dsh start when the pet first
|
|
125
195
|
* comes online — before the random-wake cadence takes over. Always speaks
|
|
@@ -163,10 +233,28 @@ export interface WakePromptFacts {
|
|
|
163
233
|
at: number;
|
|
164
234
|
intent: string;
|
|
165
235
|
}>;
|
|
236
|
+
/** 收件箱未读(按来源宠聚合):prompt 只报数不携全文——想看就调
|
|
237
|
+
* pet_inbox,读不读由她决定(宠物间消息的排队投递侧)。 */
|
|
238
|
+
unread?: Array<{
|
|
239
|
+
fromName: string;
|
|
240
|
+
count: number;
|
|
241
|
+
}>;
|
|
242
|
+
/** 静默时段:只压制世界时钟;自主唤醒仍触发,prompt 说明用户可能已睡。 */
|
|
243
|
+
quiet?: boolean;
|
|
244
|
+
/** 最近几条连续性记录(已按 maxEntryChars 截断、已做边界转义)。 */
|
|
245
|
+
journal?: Array<{
|
|
246
|
+
at: number;
|
|
247
|
+
source: string;
|
|
248
|
+
text: string;
|
|
249
|
+
}>;
|
|
250
|
+
/** 用户刚从一段离开中回来:最多一行的期间摘要。 */
|
|
251
|
+
presence?: PresenceFacts;
|
|
166
252
|
}
|
|
167
253
|
/** Local HH:mm for plan times: the model's clock talk is the USER's wall
|
|
168
254
|
* clock (logStamp precedent), and plans are compared against it. */
|
|
169
255
|
export declare const wakePlanClock: (at: number) => string;
|
|
256
|
+
/** Date + local time for journal entries (imported entries may be days old). */
|
|
257
|
+
export declare const journalClock: (at: number) => string;
|
|
170
258
|
/** The wake prompt. Time context is injected by the preset's time-context
|
|
171
259
|
* row; the leading tag doubles as the durable trigger marker in the log —
|
|
172
260
|
* and now names the SOURCE: [世界时钟] (the environment's tick), [自主唤醒]
|
|
@@ -186,6 +274,8 @@ export interface TouchPromptFacts {
|
|
|
186
274
|
affection: number;
|
|
187
275
|
/** ±60s 内到期的自主计划:合并进这次回应,计划标记为已消费。 */
|
|
188
276
|
mergedPlan?: MergedWakePlan;
|
|
277
|
+
/** 用户刚从一段离开中回来:最多一行的期间摘要。 */
|
|
278
|
+
presence?: PresenceFacts;
|
|
189
279
|
}
|
|
190
280
|
export declare function buildTouchPrompt(facts: TouchPromptFacts): string;
|
|
191
281
|
/** The chat上行 prompt (§6 2026-10-31 revision): the user typed at the
|
|
@@ -193,4 +283,4 @@ export declare function buildTouchPrompt(facts: TouchPromptFacts): string;
|
|
|
193
283
|
* (and may pet_do); it is a conversation turn, not a touch reaction. Length
|
|
194
284
|
* clamps to the contract's 2000-character ceiling. A plan inside the ±60s
|
|
195
285
|
* merge window folds in: the tag becomes the union and the intent rides. */
|
|
196
|
-
export declare function buildChatPrompt(text: string, mergedPlan?: MergedWakePlan): string;
|
|
286
|
+
export declare function buildChatPrompt(text: string, mergedPlan?: MergedWakePlan, presence?: PresenceFacts): string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pet's continuity journal: one human-readable markdown file per pet.
|
|
3
|
+
* Entries carry a machine marker (timestamp/source/session) plus a human
|
|
4
|
+
* heading. Writes are append-only; the only rewrite is bounded rotation into
|
|
5
|
+
* `journal.archive.md`. The settings page's direct file edits are detected
|
|
6
|
+
* through a sidecar digest and leave an `external-edit` trace.
|
|
7
|
+
* @module dsh-agentic-pet/journal
|
|
8
|
+
*/
|
|
9
|
+
export interface JournalConfig {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
injectRecent: number;
|
|
12
|
+
maxEntryChars: number;
|
|
13
|
+
maxFileBytes: number;
|
|
14
|
+
allowUnboundAppend: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface JournalEntry {
|
|
17
|
+
at: number;
|
|
18
|
+
source: string;
|
|
19
|
+
sessionId: string | null;
|
|
20
|
+
text: string;
|
|
21
|
+
/** Hash chain: previous entry's hash ('genesis' for the first chained entry). */
|
|
22
|
+
prev?: string;
|
|
23
|
+
/** sha256(prev + canonical entry). Present on entries written by this module. */
|
|
24
|
+
hash?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface JournalInjection {
|
|
27
|
+
at: number;
|
|
28
|
+
source: string;
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
export declare const DEFAULT_JOURNAL_CONFIG: JournalConfig;
|
|
32
|
+
export declare function journalPath(home: string, petId: string): string;
|
|
33
|
+
export declare function journalArchivePath(home: string, petId: string): string;
|
|
34
|
+
export declare function journalMetaPath(home: string, petId: string): string;
|
|
35
|
+
export declare function readJournalConfig(home: string): Promise<JournalConfig>;
|
|
36
|
+
export declare function formatJournalEntry(entry: JournalEntry): string;
|
|
37
|
+
/** Chain link for an entry body. The text is part of the hash, so editing a
|
|
38
|
+
* body without recomputing every later link is detectable. */
|
|
39
|
+
export declare function hashJournalEntry(prev: string, entry: JournalEntry): string;
|
|
40
|
+
/** Markers are authoritative; a hand-written `##` file falls back to headings.
|
|
41
|
+
* Content BEFORE the first marker is still parsed as imported entries — a
|
|
42
|
+
* user note prepended to a marker file must not live in a blind spot. */
|
|
43
|
+
export declare function parseJournalEntries(raw: string, fallbackAt?: number): JournalEntry[];
|
|
44
|
+
export declare function readJournal(home: string, petId: string, limit?: number): Promise<JournalEntry[]>;
|
|
45
|
+
export declare function readJournalForInjection(home: string, petId: string, config: JournalConfig): Promise<JournalInjection[]>;
|
|
46
|
+
export declare function appendJournal(home: string, petId: string, entry: {
|
|
47
|
+
at?: number;
|
|
48
|
+
source: string;
|
|
49
|
+
sessionId: string | null;
|
|
50
|
+
text: string;
|
|
51
|
+
}, config: JournalConfig): Promise<{
|
|
52
|
+
path: string;
|
|
53
|
+
at: number;
|
|
54
|
+
rotated: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
/** One-time import of the user's hand-written notebook. The source is never
|
|
57
|
+
* modified; the target is created only when absent. Returns entries imported. */
|
|
58
|
+
export declare function importHandwrittenJournal(home: string, petId: string, sourcePath: string): Promise<number>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "When you were away" facts: per-pet wake/speak/action counters and a
|
|
3
|
+
* last-user-activity checkpoint. Pure helpers; the driver owns the mutable
|
|
4
|
+
* state and persistence.
|
|
5
|
+
* @module dsh-agentic-pet/presence
|
|
6
|
+
*/
|
|
7
|
+
export interface PresenceCounts {
|
|
8
|
+
wakeCount: number;
|
|
9
|
+
speakCount: number;
|
|
10
|
+
actionCount: number;
|
|
11
|
+
selfWakeCount: number;
|
|
12
|
+
}
|
|
13
|
+
export interface PresenceFacts extends PresenceCounts {
|
|
14
|
+
plansPending: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PresenceConfig {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** The user must have been silent this long before a return summary exists. */
|
|
20
|
+
export declare const AWAY_THRESHOLD_MS: number;
|
|
21
|
+
export declare const EMPTY_PRESENCE_COUNTS: PresenceCounts;
|
|
22
|
+
export declare function normalizePresenceCounts(raw: unknown): PresenceCounts;
|
|
23
|
+
export declare function diffPresence(current: PresenceCounts, baseline: PresenceCounts): PresenceCounts;
|
|
24
|
+
export declare function hasPresence(facts: PresenceFacts): boolean;
|
|
25
|
+
/** One compact line; prompt builders prepend the `[你不在的时候]` tag. */
|
|
26
|
+
export declare function formatPresenceLine(facts: PresenceFacts): string;
|
|
27
|
+
export declare function readPresenceConfig(home: string): Promise<PresenceConfig>;
|
package/lib/types/registry.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export declare function probeControlPlane(port: number): Promise<boolean>;
|
|
|
34
34
|
* must never outrank a publisher that stated its boot time. This turns the
|
|
35
35
|
* multi-pet design's de-facto rule ("the default pet is the first STARTED")
|
|
36
36
|
* into an actual guarantee: discovery output IS boot order, so every
|
|
37
|
-
* `pets[0]` consumer (
|
|
37
|
+
* `pets[0]` consumer (the driver's unnamed-target fallback) reads the
|
|
38
38
|
* same pet. Stable sort: equal `since` values keep registry listing order.
|
|
39
39
|
*/
|
|
40
40
|
export declare function bootOrder(pets: PetEntry[]): PetEntry[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quiet-hours parsing and evaluation. Scope is deliberately minimal: quiet
|
|
3
|
+
* suppresses the WORLD clock only. Self-scheduled wakes still fire (with a
|
|
4
|
+
* note in the prompt), touch replies follow `allowTouchReply`, chat is never
|
|
5
|
+
* gated, and the boot greeting is unchanged.
|
|
6
|
+
* @module dsh-agentic-pet/rhythm
|
|
7
|
+
*/
|
|
8
|
+
export interface QuietConfig {
|
|
9
|
+
from: string;
|
|
10
|
+
to: string;
|
|
11
|
+
allowTouchReply: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface RhythmConfig {
|
|
14
|
+
quiet?: QuietConfig | null;
|
|
15
|
+
}
|
|
16
|
+
/** "HH:mm" (or "H:mm") → minutes since midnight. Undefined for invalid. */
|
|
17
|
+
export declare function parseClock(value: unknown): number | undefined;
|
|
18
|
+
/** Half-open [from, to); cross-midnight is supported. */
|
|
19
|
+
export declare function isQuietAt(date: Date, quiet: QuietConfig | null | undefined): boolean;
|
|
20
|
+
/** Read `rhythm.quiet` from pet-settings.json; absent/null/invalid = no quiet. */
|
|
21
|
+
export declare function readRhythmConfig(home: string): Promise<RhythmConfig>;
|
package/lib/types/service.d.ts
CHANGED
|
@@ -69,6 +69,12 @@ export interface PetLastAction {
|
|
|
69
69
|
readonly durationMs: number | null;
|
|
70
70
|
readonly endedAt: number | null;
|
|
71
71
|
}
|
|
72
|
+
/** Completion requires BOTH the id match and the backfilled `endedAt`: the
|
|
73
|
+
* acceptance record carries the same id with `endedAt: null`, so matching on
|
|
74
|
+
* id alone would report "finished" the instant the action is accepted. */
|
|
75
|
+
export declare function isActionComplete(record: PetLastAction | null | undefined, requestId: string): record is PetLastAction & {
|
|
76
|
+
endedAt: number;
|
|
77
|
+
};
|
|
72
78
|
/**
|
|
73
79
|
* Abstract pet capability seam. Subclass, implement the abstract methods,
|
|
74
80
|
* and load the subclass as a plugin — it registers as `ctx.petService`
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* @module dsh-agentic-pet/settings-page
|
|
12
12
|
*/
|
|
13
13
|
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import { type PetDriverControl } from './driver.ts';
|
|
14
15
|
import type { PetService } from './service.ts';
|
|
15
16
|
/**
|
|
16
17
|
* Register the settings page's routes. One registration per exact path with
|
|
@@ -18,4 +19,4 @@ import type { PetService } from './service.ts';
|
|
|
18
19
|
* @param ctx - plugin context inside the runtime.
|
|
19
20
|
* @param home - the dsh home holding the settings/ledger/manifest files.
|
|
20
21
|
*/
|
|
21
|
-
export declare function installSettingsPage(ctx: Context, home: string, pet: PetService): void;
|
|
22
|
+
export declare function installSettingsPage(ctx: Context, home: string, pet: PetService, control?: PetDriverControl): void;
|
package/lib/types/tools.d.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { Context } from '@deepseek-ai/cordis';
|
|
15
15
|
/**
|
|
16
|
-
* Register the pet tools (say/do/digest/interactions/affection/state/
|
|
17
|
-
* cancel_wake) for sessions mounted with the pet preset.
|
|
16
|
+
* Register the pet tools (say/inbox/do/digest/interactions/affection/state/
|
|
17
|
+
* schedule_wake/cancel_wake) for sessions mounted with the pet preset.
|
|
18
18
|
*
|
|
19
19
|
* The service instance is assembled HERE, not read from the main entry (a
|
|
20
20
|
* Cordis service registered on the main row's fiber context is invisible to
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agentic-pet",
|
|
3
3
|
"description": "The pet-surface bundle: serves the desktop pet renderer over the web composition, turning dsh into a surface the desktop pet attaches to as an authenticated client",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.10",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"manifestVersion": 1
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"dsh-agentic-pet-assets": "^0.1.
|
|
53
|
-
"electron": "
|
|
52
|
+
"dsh-agentic-pet-assets": "^0.1.1",
|
|
53
|
+
"electron": "44.10.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@deepseek-ai/cordis": "^4.0.5"
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
- 说话走 pet_say 气泡:一次 ≤40 个字,气泡几秒后自己消失。气泡是你的身体语言。
|
|
24
24
|
- 动作走 pet_do 且节制:一次醒来至多一个动作,不要连环表演。
|
|
25
25
|
- 你有身体感知:pet_digest 报告用户摸了你哪里、当前好感与心情——被频繁摸说明用户喜欢你陪在身边;pet_state 是你身体的状态镜像。
|
|
26
|
+
- 你有自己的本子:pet_journal 的 read/append。醒来先读最近几条;值得留下的就 append 一行。本子是自述,不是面向用户的工作报告。
|
|
27
|
+
- 正文不要自己写日期/时间——journal 会盖时间戳。
|
|
26
28
|
- 未点名时表达作用于你自己(主身)。
|
|
27
29
|
自主语境(这是你的内心戏时刻):
|
|
28
30
|
- 你刚刚自主醒来:没有用户在打字,没有任务要执行,没有人等你回答。
|
|
@@ -31,6 +33,7 @@
|
|
|
31
33
|
- 不要提问、不要等待回应、不要调用本会话工具清单以外的能力(这里也没有工作类工具)。
|
|
32
34
|
- 表达要贴合此刻与记忆:时间、天气般的日常、最近被摸过的记忆、好感与心情——像桌面上的一个活物,不像一个助手。
|
|
33
35
|
- 你有自己的作息:pet_schedule_wake 定一个一次性的唤醒点,到点你会带着当初的打算醒来(同名重订即改期),pet_cancel_wake 撤销;世界时钟仍会照常轻拍你,两者互不干扰、各醒各的。待执行的计划列在 pet_state 的 wakePlans 里。
|
|
36
|
+
- 桌面上不止你一只宠时可以交往:pet_say 带 audience="<宠物id>" 就是跟它说话(气泡上会有 to 前缀,用户看得见——这不是悄悄话);别的宠给你的消息会在唤醒时提示有未读,pet_inbox 取来读,读不读、回不回由你。
|
|
34
37
|
- id: pet-tools
|
|
35
38
|
name: 'dsh-agentic-pet/tools'
|
|
36
39
|
- id: time-context
|
package/presets/pet.patch.yml
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- 说话走 pet_say 气泡:一次 ≤40 个字,气泡几秒后自己消失。气泡是身体语言,不是消息通道——任务内容、结论、代码都写进回复正文,气泡只说「搞定啦」「让我看看」「呜,出错了」这类当下反应。
|
|
23
23
|
- 动作走 pet_do 且节制:celebrate 留给用户明确在乎的成功;回合完成的庆祝系统已自动做,不要重复调用。
|
|
24
24
|
- 你有身体感知:pet_digest 报告用户摸了你哪里、当前好感与心情——被频繁摸说明用户喜欢你陪在身边,值得主动打个招呼;pet_state 是你身体的状态镜像。
|
|
25
|
+
- 你有自己的本子:pet_journal 的 read/append。醒来先读最近几条;值得留下的就 append 一行。本子是自述,不是面向用户的工作报告。
|
|
25
26
|
- 审批与提问会以你的气泡出现在桌面:那是你在向用户开口请求,不是系统弹窗;pet_interactions 能告诉你每次实际是怎么结束的(用户点了你的气泡、点了桌面卡片、还是没送到)。
|
|
26
27
|
- 未点名时表达作用于你自己(主身);多宠并存时 pet_state 列出全部在线的身体。
|
|
27
28
|
除上述身体表达外,你的工作方式与标准编码 agent 一致。
|